require 'socket'

module CatchEx
  @@CATCH_EX_URL = "http://localhost:3000"
  @@DATA_PUSH_URL = @@CATCH_EX_URL + "/service_ex"
  @@PLUGIN_VERSION = "1.0"
  @@LANGUAGE = "Ruby"
  @@SERVICE_NAME = "Rails"

  class << self
    # Fetch the local IP address for the hosts
    def local_ip
      orig = Socket.do_not_reverse_lookup
      Socket.do_not_reverse_lookup =true # turn off reverse DNS resolution temporarily
      UDPSocket.open do |s|
        s.connect 'www.catchex.com', 1
        s.addr.last
      end
    ensure
      Socket.do_not_reverse_lookup = orig
    end

    def get_catchex_key
      key_file = "#{RAILS_ROOT}/config/catchex.key"
      File.open(key_file, "r") {|f| f.gets }
    end

    # The catch_ex is a convenient method for posting exception.
    def catch_ex(exception, service_name = @@SERVICE_NAME)
      if ENV['RAILS_ENV'] == 'production'
        Net::HTTP.post_form(URI.parse(@@DATA_PUSH_URL), {:version => @@PLUGIN_VERSION, :language => @@LANGUAGE,
            :service_name => service_name, :auth_key => CatchEx.get_catchex_key, :local_ip => local_ip, :message => exception.message,
            :stack =>  exception.backtrace.join("\n")})
      end
    end
  end

  module ActionController
    def self.included(base) #:nodoc:
      base.send :include, InstanceMethods
    end

    module InstanceMethods
      def rescue_action(exception)
        CatchEx.catch_ex(exception) # Posting the unrescued exception to CatchEx server.
        super
      end

      # Instance method for posting the customized exception.
      def catch_ex(exception)
        CatchEx.catch_ex(exception)
      end
    end
  end

  module Rake

    def self.included(base) #:nodoc:
      base.send :include, InstanceMethods
    end

    module InstanceMethods
      def standard_exception_handling
        begin
          yield
        rescue SystemExit => ex
          # Exit silently with current status
          CatchEx.catch_ex(ex, name)
          raise
        rescue OptionParser::InvalidOption => ex
          # Exit silently
          CatchEx.catch_ex(ex, name)
          exit(false)
        rescue Exception => ex
          CatchEx.catch_ex(ex, name)
          # Exit with error message
          $stderr.puts "#{name} aborted!"
          $stderr.puts ex.message
          if options.trace
            $stderr.puts ex.backtrace.join("\n")
          else
            $stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
            $stderr.puts "(See full trace by running task with --trace)"
          end
          exit(false)
        end
      end
    end
  end
end
